home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1999 May: Tool Chest / Developer CD Series Tool Chest (Apple Computer)(May 1999).iso / Tool Chest / Files / UnmountIt 1.2 / UnmountIt.p < prev    next >
Encoding:
Text File  |  1995-06-20  |  43.3 KB  |  1,388 lines  |  [TEXT/MWPS]

  1. {$N-}
  2.  
  3. {$IFC UNDEFINED THINK_Pascal}
  4. {$ELSEC}
  5. {$I-}
  6. {$ENDC}
  7.  
  8.  
  9. { UnmountIt.p }
  10. { by Jim Luther }
  11. { Apple Developer Technical Support }
  12. { Copyright © 1992-1995, Apple Computer Inc. }
  13. { All rights reserved }
  14.  
  15. { 03 Mar 93    JML        v1.0GM    Froze code and released version 1.0. }
  16. { 03 May 93    JML        v1.1d1    Start on 1.1 update to add toggle feature. }
  17. { 03 May 93    JML        v1.1d1    Moved call to GetServerState in kUnmountItem from before }
  18. {                                        volume selection to after volume selection. }
  19. { 03 May 93    JML        v1.1d1    Added menu handling code and modified resource for }
  20. {                                        kRestartSharing menu item. }
  21. { 04 May 93    JML        v1.1d2    Added restart handling code to AEOpenHandler and}
  22. {                                        added auto-quit code to Idle after restart from OpenApp. }
  23. { 04 May 93    JML        v1.1d2    Added code to check for sessions before restart. }
  24. { 04 May 93    JML        v1.1b1    It all worked... declare it beta.  It's great to be }
  25. {                                        the engineer, tester, and designer (grin). }
  26. { 04 May 93    JML        v1.1b1    Oh yeah, I added Brigham's cool color icons. }
  27. { 24 Mar 94    JML        v1.1GM    Released (kind of). }
  28. { 21 May 95    JML        v1.2b1    Made changes so UnmountIt compiles with Metrowerks Pascal and }
  29. {                            the Universal Interfaces. }
  30. { 21 May 95    JML        v1.2b1    UnmountIt now checks for the AppleShare and for Macintosh }
  31. {                            File Sharing 7.6.1 or later. If it finds either, then it alerts }
  32. {                            the user and quits. }
  33.  
  34.  
  35. PROGRAM UnmountIt;
  36.  
  37.     USES
  38. {$IFC UNDEFINED THINK_Pascal}
  39.         Types, QuickDraw, Events, OSUtils, Files, Devices, DeskBus, DiskInit,{…}
  40.         Disks, Errors, Memory, Retrace, SegLoad, Serial, ShutDown,{…}
  41.         Slots, Sound, Start, Timer, Controls, Windows, TextEdit, Dialogs,{…}
  42.         Fonts, Lists, Menus, Resources, Scrap, ToolUtils, StandardFile, Script,{…}
  43.         Packages, Traps, AppleTalk, PPCToolbox, Processes, EPPC, Notification,{…}
  44.         AppleEvents, GestaltEqu, Balloons, Folders, ServerControlIntf;
  45. {$ELSEC}
  46.         AppleTalk, PPCToolbox, Processes, EPPC, Notification, AppleEvents, Traps, {…}
  47.         Balloons, Folders, ServerControlIntf;
  48. {$ENDC}
  49.  
  50.     CONST
  51.         kParam0 = '^0';
  52.         kParam1 = '^1';
  53.         kParam2 = '^2';
  54.         kParam3 = '^3';
  55.  
  56.         kAboutBox = 1000;                { AboutBox alert }
  57.         kStopOK = 1001;
  58.         kNoteOK = 1002;
  59.         kCautionCancelOK = 1003;
  60.         kCautionStopContinue = 1004; {not used at this time}
  61.         kCautionCancelSave = 1005; {not used at this time}
  62.  
  63.         kAlertStrings = 1000;
  64.         kBadSystem = 1;
  65.         kNoAppleEvents = 2;
  66.         kAEHandlerInstallError = 3;
  67.         kInfoError = 4;
  68.         kStartupDisk = 5;
  69.         kNonEjectableVol = 6;
  70.         kPeopleConnected = 7;
  71.         kSCShutdownError = 8;
  72.         kStartingStoppingError = 9;
  73.         kFileBusyOnVol = 10;
  74.         kNetTrashFull = 11;
  75.         kPeopleConnectedRestart = 12;
  76.         kIsAppleShareServer = 13;
  77.         kIsNewFileSharingServer = 14;
  78.  
  79.         kStartingStoppingStrings = 1001;
  80.         kStartingStr = 1;
  81.         kOnStr = 2;
  82.         kStoppingStr = 3;
  83.         kOffStr = 4;
  84.  
  85.         kHelp = 2000;            { help menu item string and dialog }
  86.  
  87.         kCustomGetVolumeDLOG = 2001;
  88.  
  89.         kResumeMask = 1;    { bit of message field for resume vs. suspend }
  90.  
  91.         kMBarID = 128;
  92.  
  93.         kAppleMenu = 128;    { the Apple menu }
  94.  
  95.         kFileMenu = 129;    { the File menu }
  96.         kUnmountItem = 1;
  97.         kRestartSharing = 2;
  98.         kQuitItem = 4;
  99.  
  100.     TYPE
  101.         volQElemPtr = ^volQElem;
  102.         volQElem = RECORD
  103.                 qLink: QElemPtr;
  104.                 qType: Integer;
  105.                 volumeRefNum: Integer;
  106.                 driveNum: Integer;
  107.                 volumeName: Str255;
  108.                 ejected: Boolean;
  109.                 ejectable: Boolean;
  110.                 shared: Boolean;
  111.                 networkVol: Boolean;
  112.                 netTrashVRefNum: Integer;
  113.                 netTrashDirID: LongInt;
  114.                 netTrashEmpty: Boolean;
  115.             END;
  116.  
  117.     VAR
  118.         gMymenu: Handle;    { my menu bar handle }
  119.         gAppleMenuHandle: MenuHandle;
  120.         gFileMenuHandle: MenuHandle;
  121.         gQuit: Boolean;
  122.         gInBackground: Boolean;
  123.         gOurSN: ProcessSerialNumber;
  124.         gHelpItem: Integer;    { 0 if no help item }
  125.         gHasAppleEvents: Boolean;
  126.         gHasServerDispatch: Boolean; { TRUE if we can make server control calls }
  127.  
  128.         gServerState: Integer; { the current state of the server - checked right before any unmount }
  129.         gNeedToCheckForSharing: Boolean;    { check for sharing at least once per list of volumes }
  130.         gTurnServerBackOn: Boolean;    { if TRUE, need to turn server on after unmount/eject }
  131.         gNoSharedVolumes: Boolean;    { if TRUE, don't unmount any shared volumes in this list }
  132.         gRestarting: Boolean;    { if TRUE, then we're restarting the server }
  133.         gQuitAfterRestart: Boolean;    { if TRUE, then quit after restarting the server }
  134. {••• Need to ignore unmount requests while restarting and ignore restart requests }
  135. {••• while unmounting. }
  136.         gVolsEnqueued: Boolean;    { if TRUE, there are volumes to unmount }
  137.         gVolQHdr: QHdr;    { holds list of volumes to unmount/eject }
  138.  
  139.         gFirstAE: AEEventID;
  140.  
  141. {==============================================================================}
  142.  
  143. { The NumToolboxTraps, GetTrapType, and TrapAvailable functions are from }
  144. { Inside Macintosh Volume VI }
  145.  
  146. {$S Main}
  147.     FUNCTION NumToolboxTraps: Integer;
  148.     BEGIN
  149.         IF NGetTrapAddress(_InitGraf, ToolTrap) = NGetTrapAddress($AA6E, ToolTrap) THEN
  150.             NumToolboxTraps := $200
  151.         ELSE
  152.             NumToolboxTraps := $400;
  153.     END;
  154.  
  155. {------------------------------------------------------------------------------}
  156.  
  157. {$S Main}
  158.     FUNCTION GetTrapType (theTrap: Integer): TrapType;
  159.         CONST
  160.             TrapMask = $0800;
  161.     BEGIN
  162.         IF BAND(theTrap, TrapMask) > 0 THEN
  163.             GetTrapType := ToolTrap
  164.         ELSE
  165.             GetTrapType := OSTrap;
  166.     END;
  167.  
  168. {------------------------------------------------------------------------------}
  169.  
  170. {$S Main}
  171.     FUNCTION TrapAvailable (theTrap: Integer): Boolean;
  172.         VAR
  173.             tType: TrapType;
  174.     BEGIN
  175.         tType := GetTrapType(theTrap);
  176.         IF tType = ToolTrap THEN
  177.             BEGIN
  178.                 theTrap := BAND(theTrap, $07FF);
  179.                 IF theTrap >= NumToolboxTraps THEN
  180.                     theTrap := _Unimplemented;
  181.             END;
  182.         TrapAvailable := NGetTrapAddress(theTrap, tType) <> NGetTrapAddress(_Unimplemented, ToolTrap)
  183.     END;
  184.  
  185. {------------------------------------------------------------------------------}
  186.  
  187. {$S Main}
  188.     FUNCTION MyCustomGetVolumeFileFilter (myPB: CInfoPBPtr;
  189.                                     myDataPtr: PTR): BOOLEAN;
  190.     BEGIN
  191.         MyCustomGetVolumeFileFilter := TRUE;
  192.         {list volumes only}
  193.         IF BTst(myPB^.ioFlAttrib, 4) AND (myPB^.ioDrParID = fsRtParID) THEN
  194.             MyCustomGetVolumeFileFilter := FALSE;
  195.     END;
  196.  
  197. {------------------------------------------------------------------------------}
  198.  
  199. {$S Main}
  200.     FUNCTION MyCustomGetVolumeHook (item: INTEGER;
  201.                                     theDialog: DialogPtr;
  202.                                     myDataPtr: PTR): INTEGER;
  203.         VAR
  204.             dlgPeek: WindowPeek;
  205.             itemType: INTEGER;
  206.             itemToChange: Handle;
  207.             itemBox: Rect;
  208.             buttonTitle: Str255;
  209.     BEGIN
  210.         MyCustomGetVolumeHook := item;        {default, except in special cases below}
  211.  
  212.         {CustomGet calls dialog hook for both}
  213.         { main and subsidiary dialog boxes. Check }
  214.         { here that the refCon indicates dialog }
  215.         { record describes main dialog box}
  216.         dlgPeek := WindowPeek(theDialog);
  217.         IF OSType(dlgPeek^.refCon) = sfMainDialogRefCon THEN
  218.             BEGIN
  219.                 CASE item OF
  220.                     sfHookFirstCall: 
  221.                         BEGIN
  222.                             {set button title and goto desktop}
  223.                             buttonTitle := 'Select';
  224.                             GetDialogItem(theDialog, sfItemOpenButton, itemType, itemToChange, itemBox);
  225.                             SetControlTitle(ControlHandle(itemToChange), buttonTitle);
  226.                             MyCustomGetVolumeHook := sfhookGotoDesktop;
  227.                         END;
  228.  
  229.                     {Map cmd-D to a NullEvent}
  230.                     sfhookGotoDesktop: 
  231.                         MyCustomGetVolumeHook := sfHookNullEvent;
  232.  
  233.                     {sfHookRebuildList will work also}
  234.                     sfHookChangeSelection: 
  235.                         MyCustomGetVolumeHook := sfhookGotoDesktop;
  236.  
  237.                     {Map cmd-left Arrow to a NullEvent}
  238.                     sfHookGotoNextDrive: 
  239.                         MyCustomGetVolumeHook := sfHookNullEvent;
  240.  
  241.                     {Map cmd-right Arrow to a NullEvent}
  242.                     sfHookGotoPrevDrive: 
  243.                         MyCustomGetVolumeHook := sfHookNullEvent;
  244.  
  245.                     {return the open button's value}
  246.                     sfItemOpenButton, sfHookOpenFolder: 
  247.                         MyCustomGetVolumeHook := sfItemOpenButton;
  248.                     OTHERWISE
  249.                         ;
  250.                 END;
  251.             END;
  252.     END;
  253.  
  254. {------------------------------------------------------------------------------}
  255.  
  256. {$S Main}
  257.     PROCEDURE DoCustomGetVolume (VAR volRefNum: Integer);
  258.         VAR
  259.             myCustGetFileReply: StandardFileReply;
  260.             myTypeListNo2: SFTypeList;
  261.             myNumTypes: INTEGER;
  262.             myDlogCoords: Point;
  263.             myModalFilter: ModalFilterYDProcPtr;
  264.             myActiveList: ActivationOrderListPtr;
  265.             myActivateProc: ActivateYDProcPtr;
  266.     BEGIN
  267.         {for first phase of filtering, by file type,}
  268.         { pass files of all types}
  269.         myNumTypes := -1;
  270.         {(-1,-1) for automatic centering of dialog on screen }
  271.         myDlogCoords.h := -1;
  272.         myDlogCoords.v := -1;
  273.         myModalFilter := NIL;
  274.         myActiveList := NIL;
  275.         myActivateProc := NIL;
  276.  
  277.         CustomGetFile(@MyCustomGetVolumeFileFilter, myNumTypes, @myTypeListNo2, myCustGetFileReply, kCustomGetVolumeDLOG, myDlogCoords, @MyCustomGetVolumeHook, myModalFilter, myActiveList, myActivateProc, @myCustGetFileReply);
  278.  
  279.         IF (myCustGetFileReply.sfGood) AND (myCustGetFileReply.sfIsVolume) THEN
  280.             volRefNum := myCustGetFileReply.sfFile.vRefNum
  281.         ELSE
  282.             volRefNum := 0;
  283.     END;
  284.  
  285. {------------------------------------------------------------------------------}
  286.  
  287. {$S Main}
  288.     { This function works ONLY with File Sharing file servers released before }
  289.     { File Sharing 7.6.1. DO NOT use it with File Sharing 7.6.1 (or later) or }
  290.     { with AppleShare file servers! }
  291.     FUNCTION numSessions: Integer;
  292.     BEGIN
  293.         { Make sure the file server is up and running }
  294.         IF (LongIntPtr($B50)^ <> 0) AND (LongIntPtr($B50)^ <> -1) THEN
  295.             { it is, so see how many sessions are active }
  296.             numSessions := IntegerPtr(ORD4(LongIntPtr($B50)^) + $10)^
  297.         ELSE { it isn't, so return 0 }
  298.             numSessions := 0;
  299.     END;
  300.  
  301. {------------------------------------------------------------------------------}
  302.  
  303.     FUNCTION ReleaseFolder (vRefNum: INTEGER;        { volume reference number }
  304.                                     folderType: OSType    { always kTrashFolderType }
  305.                                     ): OSErr;
  306.     INLINE
  307.         $700B, $A823;
  308.  
  309. {------------------------------------------------------------------------------}
  310.  
  311. {$S Main}
  312.     FUNCTION FindTrash (VAR trashVRefNum: Integer;
  313.                                     VAR trashDirID: LongInt;
  314.                                     VAR trashEmpty: Boolean): OSErr;
  315.         VAR
  316.             err: OSErr;
  317.             cPB: CInfoPBRec;
  318.             trashName: Str31; { not really needed, but there's that File Sharing bug... }
  319.     BEGIN
  320.         err := FindFolder(trashVRefNum, kTrashFolderType, FALSE, trashVRefNum, trashDirID);
  321.         IF err = noErr THEN
  322.             BEGIN
  323.                 cPB.ioNamePtr := @trashName;
  324.                 cPB.ioVRefNum := trashVRefNum;
  325.                 cPB.ioDirID := trashDirID;
  326.                 cPB.ioFDirIndex := -1;
  327.                 err := PBGetCatInfoSync(@cPB);
  328.                 IF err = noErr THEN
  329.                     BEGIN
  330.                         trashEmpty := cPB.ioDrNmFls = 0;
  331.                     END;
  332.             END
  333.         ELSE IF (err = fnfErr) THEN
  334.             BEGIN
  335.                 trashVRefNum := 0;
  336.                 trashDirID := 0;
  337.                 trashEmpty := true;
  338.                 err := noErr;
  339.             END;
  340.         FindTrash := err;
  341.     END;
  342.  
  343. {------------------------------------------------------------------------------}
  344.  
  345. {$S Main}
  346.     FUNCTION GetVolumeInfo (volInfoPtr: volQElemPtr): OSErr;
  347.         VAR
  348.             volParmsInfoBuffer: GetVolParmsInfoBuffer;
  349.             drvQElem: DrvQElPtr;
  350.             pb: HParamBLockRec;
  351.             err: OSErr;
  352.     BEGIN
  353.         pb.ioNamePtr := @volInfoPtr^.volumeName;
  354.         pb.ioVRefNum := volInfoPtr^.volumeRefNum;
  355.         pb.ioVolIndex := 0;
  356.         err := PBHGetVInfoSync(@pb);
  357.         IF (err = noErr) THEN
  358.             BEGIN
  359.                 volInfoPtr^.driveNum := pb.ioVDrvInfo;
  360.                 { is it ejected? }
  361.                 volInfoPtr^.ejected := (pb.ioVDrvInfo = 0) AND (pb.ioVDRefNum > 0);
  362.  
  363.                 drvQElem := DrvQElPtr(GetDrvQHdr^.qHead);    { get the first drive queue entry }
  364.                 { find the drive queue entry for this volume }
  365.                 WHILE (drvQElem <> NIL) & (drvQElem^.dQDrive <> pb.ioVDrvInfo) DO
  366.                     drvQElem := DrvQElPtr(drvQElem^.qLink);
  367.                 { is it ejectable? }
  368.                 volInfoPtr^.ejectable := (drvQElem <> NIL) & (Ptr(ORD4(drvQElem) - 3)^ <> 8);
  369.             END;
  370.  
  371.         pb.ioNamePtr := NIL;
  372.         pb.ioVRefNum := volInfoPtr^.volumeRefNum;
  373.         pb.ioBuffer := @volParmsInfoBuffer;
  374.         pb.ioReqCount := sizeof(GetVolParmsInfoBuffer);
  375.         { Since PBHGetVolParms is always available under System 7, any errors are real errors }
  376.         err := PBHGetVolParmsSync(@pb);
  377.         { is it shared? }
  378.         volInfoPtr^.shared := (err = noErr) & BTST(volParmsInfoBuffer.vMAttrib, bHasPersonalAccessPrivileges);
  379.         volInfoPtr^.networkVol := volParmsInfoBuffer.vMServerAdr <> 0;
  380.         IF volInfoPtr^.networkVol THEN
  381.             BEGIN
  382.                 WITH volInfoPtr^ DO
  383.                     BEGIN
  384.                         netTrashVRefNum := volumeRefNum;
  385.                         err := FindTrash(netTrashVRefNum, netTrashDirID, netTrashEmpty);
  386.                     END;
  387.             END;
  388.  
  389.         GetVolumeInfo := err;    { return any errors }
  390.     END;
  391.  
  392. {------------------------------------------------------------------------------}
  393.  
  394. {$S Main}
  395.     FUNCTION ShutDownTheServer: OSErr;
  396.         VAR
  397.             scErr: OSErr;
  398.             scPB: SCParamBlockRec;
  399.             pdsName: Str255;
  400.             hPB: HParamBlockRec;
  401.     BEGIN
  402.         scPB.disconnectPB.scCode := SCShutDown;
  403.         scPB.disconnectPB.scNumMinutes := 0; { shut down NOW! }
  404.         scPB.disconnectPB.scFlags := 0;
  405.         scPB.disconnectPB.scMessagePtr := NIL;
  406.         scErr := SyncServerDispatch(@scPB);
  407.         ShutDownTheServer := scErr;
  408.     END;
  409.  
  410. {------------------------------------------------------------------------------}
  411.  
  412. {$S Main}
  413.     PROCEDURE StartTheServer;
  414.         VAR
  415.             scPB: SCParamBlockRec;
  416.     BEGIN
  417.         scPB.startPB.scCode := SCStartServer;
  418.         scPB.startPB.scStartSelect := kCurInstalled;
  419.         scPB.startPB.scEventSelect := kFinderExtn;
  420.         IF SyncServerDispatch(@scPB) <> noErr THEN
  421.             ; { who cares }
  422.     END;
  423.  
  424. {------------------------------------------------------------------------------}
  425.  
  426. {$S Main}
  427.     PROCEDURE AddToVolQueue (myFSS: FSSpec);
  428.         VAR
  429.             foundVRefNum: Integer;
  430.             foundDirID: LongInt;
  431.             volInfoPtr: volQElemPtr;
  432.             itemHit: Integer;
  433.             paramPos: Integer;
  434.             alertString, paramStr0, paramStr1: Str255;
  435.     BEGIN
  436.         { filter out folders }
  437.         IF myFSS.parID <> fsRtParID THEN
  438.             Exit(AddToVolQueue);
  439.  
  440.         { filter out the boot volume }
  441.         IF FindFolder(kOnSystemDisk, kSystemFolderType, FALSE, foundVRefNum, foundDirID) = noErr THEN
  442.             BEGIN
  443.                 IF foundVRefNum = myFSS.vRefNum THEN
  444.                     BEGIN
  445.                         { Tell user “The startup disk cannot be removed from the desktop, }
  446.                         { because it contains the active system software.”}
  447.                         GetIndString(alertString, kAlertStrings, kStartupDisk);
  448.                         ParamText(alertString, '', '', '');
  449.                         itemHit := Alert(kStopOK, NIL);
  450.                         Exit(AddToVolQueue);
  451.                     END;
  452.             END
  453.         ELSE
  454.             Exit(AddToVolQueue);
  455.  
  456.         { It's passed enough tests to create a queue element for it. }
  457.         volInfoPtr := volQElemPtr(NewPtr(LongInt(sizeof(volQElem))));
  458.         IF volInfoPtr = NIL THEN
  459.             Exit(AddToVolQueue); { this shouldn't happen, but… }
  460.  
  461.         { add the volume reference number to the queue element }
  462.         volInfoPtr^.volumeRefNum := myFSS.vRefNum;
  463.  
  464.         { and find out everything about this volume }
  465.         IF GetVolumeInfo(volInfoPtr) <> noErr THEN
  466.             BEGIN { oops, something was BAAAAAAD so skip this one }
  467.                 GetIndString(alertString, kAlertStrings, kInfoError);
  468.                 ParamText(alertString, '', '', '');
  469.                 itemHit := Alert(kStopOK, NIL);
  470.                 DisposePtr(Ptr(volInfoPtr)); { give back the memory }
  471.                 Exit(AddToVolQueue);
  472.             END;
  473.  
  474.         { check net-trash on network volumes }
  475.         IF (volInfoPtr^.networkVol) AND (NOT volInfoPtr^.netTrashEmpty) THEN
  476.             BEGIN
  477.                 { “The shared disk “^0” could not be put away, because some of }
  478.                 { its items have been moved to the Trash.  Do you want to delete }
  479.                 { those items and then put away the disk?”}
  480.                 { Cancel: then don't do it}
  481.                 { OK: continue }
  482.                 GetIndString(alertString, kAlertStrings, kNetTrashFull);
  483.                 paramPos := pos(kParam0, alertString);
  484.                 delete(alertString, paramPos, 2);
  485.                 insert(volInfoPtr^.volumeName, alertString, paramPos);
  486.                 ParamText(alertString, '', '', '');
  487.                 itemHit := Alert(kCautionCancelOK, NIL);
  488.                 IF itemHit = cancel THEN
  489.                     BEGIN
  490.                         DisposePtr(Ptr(volInfoPtr)); { give back the memory }
  491.                         Exit(AddToVolQueue);
  492.                     END;
  493.             END;
  494.  
  495.         IF (NOT volInfoPtr^.ejectable) AND (NOT volInfoPtr^.networkVol) THEN
  496.             BEGIN
  497.                 { “Are you sure you want to remove the disk “^0” from the desktop?  }
  498.                 { It will reappear on the desktop when the Macintosh is restarted.”}
  499.                 { Cancel: then don't do it}
  500.                 { OK: continue }
  501.                 GetIndString(alertString, kAlertStrings, kNonEjectableVol);
  502.                 paramPos := pos(kParam0, alertString);
  503.                 delete(alertString, paramPos, 2);
  504.                 insert(volInfoPtr^.volumeName, alertString, paramPos);
  505.                 ParamText(alertString, '', '', '');
  506.                 itemHit := Alert(kCautionCancelOK, NIL);
  507.                 IF itemHit = cancel THEN
  508.                     BEGIN
  509.                         DisposePtr(Ptr(volInfoPtr)); { give back the memory }
  510.                         Exit(AddToVolQueue);
  511.                     END;
  512.             END;
  513.  
  514.         IF volInfoPtr^.shared THEN
  515.             BEGIN
  516.                 IF gNeedToCheckForSharing THEN
  517.                     BEGIN
  518.                         gNeedToCheckForSharing := FALSE; { don't need to ask after 1st shared volume }
  519.                         CASE gServerState OF
  520.                             SCPSRunning: 
  521.                                 BEGIN
  522.                                     IF (numSessions > 0) THEN
  523.                                         BEGIN
  524.                                             { “There are people connected to this Macintosh.  }
  525.                                             { Do you want to disconnect them and unmount the }
  526.                                             { shared volume(s) anyway?”}
  527.                                             { Yes: gNoSharedVolumes := FALSE }
  528.                                             { No:  gNoSharedVolumes := TRUE  }
  529.                                             GetIndString(alertString, kAlertStrings, kPeopleConnected);
  530.                                             ParamText(alertString, '', '', '');
  531.                                             itemHit := Alert(kCautionCancelOK, NIL);
  532.                                             gNoSharedVolumes := itemHit = cancel;
  533.                                             IF NOT gNoSharedVolumes THEN
  534.                                                 IF ShutDownTheServer = noErr THEN
  535.                                                     gTurnServerBackOn := TRUE { we'll turn it back on later }
  536.                                                 ELSE
  537.                                                     BEGIN
  538.                                                         DisposePtr(Ptr(volInfoPtr)); { give back the memory }
  539.                                                         Exit(AddToVolQueue);
  540.                                                     END;
  541.                                         END
  542.                                     ELSE
  543.                                         { nobody is on, so shut down the server }
  544.                                         IF ShutDownTheServer = noErr THEN
  545.                                             gTurnServerBackOn := TRUE { we'll turn it back on later }
  546.                                         ELSE
  547.                                             BEGIN
  548.                                                 GetIndString(alertString, kAlertStrings, kSCShutdownError);
  549.                                                 paramPos := pos(kParam0, alertString);
  550.                                                 delete(alertString, paramPos, 2);
  551.                                                 insert(volInfoPtr^.volumeName, alertString, paramPos);
  552.                                                 ParamText(alertString, '', '', '');
  553.                                                 itemHit := Alert(kStopOK, NIL);
  554.                                                 { we couldn't shut down the server for some }
  555.                                                 { unknown or unexpected reason}
  556.                                                 DisposePtr(Ptr(volInfoPtr)); { give back the memory }
  557.                                                 Exit(AddToVolQueue);
  558.                                             END;
  559.                                 END;
  560.                             SCPSJustDisabled, SCPSDisabledwErr: 
  561.                                 BEGIN
  562.                                     gNoSharedVolumes := FALSE;
  563.                                     {do nothing!}
  564.                                 END;
  565.                             SCPSStartingUp: 
  566.                                 BEGIN
  567.                                     { Give this dialog and don't unmount shared volumes: }
  568.                                     {“File sharing is starting up, please wait until the Sharing Setup }
  569.                                     {control panel indicates it is on, and then try again.” OK  }
  570.                                     GetIndString(alertString, kAlertStrings, kStartingStoppingError);
  571.  
  572.                                     GetIndString(paramStr0, kStartingStoppingStrings, kStartingStr);
  573.                                     paramPos := pos(kParam0, alertString);
  574.                                     delete(alertString, paramPos, 2);
  575.                                     insert(paramStr0, alertString, paramPos);
  576.  
  577.                                     GetIndString(paramStr1, kStartingStoppingStrings, kOnStr);
  578.                                     paramPos := pos(kParam1, alertString);
  579.                                     delete(alertString, paramPos, 2);
  580.                                     insert(paramStr1, alertString, paramPos);
  581.  
  582.                                     ParamText(alertString, '', '', '');
  583.                                     itemHit := Alert(kNoteOK, NIL);
  584.                                     gNoSharedVolumes := TRUE;
  585.                                 END;
  586.                             SCPSSleeping: 
  587.                                 BEGIN
  588.                                     { This is AppleShare 3.0 which already supports unmounting on the fly.}
  589.                                     {We shouldn't get this since we don't run with anything but file sharing}
  590.                                     gNoSharedVolumes := FALSE;
  591.                                 END;
  592.                             OTHERWISE
  593.                                 BEGIN
  594.                                     { Give this dialog (maybe this isn’t needed…  if not, we’d have to cancel}
  595.                                     {the current shutdown and then shutdown(0) ourselves) and then bail: }
  596.                                     {“File sharing is shutting down, please wait until the Sharing Setup }
  597.                                     {control panel indicates it is off, and then try again.” OK  }
  598.                                     GetIndString(alertString, kAlertStrings, kStartingStoppingError);
  599.  
  600.                                     GetIndString(paramStr0, kStartingStoppingStrings, kStoppingStr);
  601.                                     paramPos := pos(kParam0, alertString);
  602.                                     delete(alertString, paramPos, 2);
  603.                                     insert(paramStr0, alertString, paramPos);
  604.  
  605.                                     GetIndString(paramStr1, kStartingStoppingStrings, kOffStr);
  606.                                     paramPos := pos(kParam1, alertString);
  607.                                     delete(alertString, paramPos, 2);
  608.                                     insert(paramStr1, alertString, paramPos);
  609.  
  610.                                     ParamText(alertString, '', '', '');
  611.                                     itemHit := Alert(kNoteOK, NIL);
  612.                                     gNoSharedVolumes := TRUE;
  613.                                 END;
  614.                         END;
  615.                     END;
  616.                 { You didn't want to kick users off? OK, no shared volumes then }
  617.                 { (dialog already told user). }
  618.                 IF gNoSharedVolumes THEN
  619.                     BEGIN
  620.                         DisposePtr(Ptr(volInfoPtr)); { give back the memory }
  621.                         Exit(AddToVolQueue);
  622.                     END;
  623.             END;
  624.  
  625.         { Hey, this volume is really going to make it into the queue }
  626.         Enqueue(QElemPtr(volInfoPtr), @gVolQHdr);
  627.         gVolsEnqueued := TRUE;
  628.  
  629.     END;
  630.  
  631. {------------------------------------------------------------------------------}
  632.  
  633. {$S Main}
  634.     FUNCTION DeleteDirectory (vRefNum: Integer;
  635.                                     dirID: LongInt): OSErr;
  636.         CONST
  637.             fsDirFlgBit = 4;
  638.         VAR
  639.             err: OSErr;
  640.             itemName: Str63;
  641.             hPB: HParamBlockRec;
  642.  
  643.         PROCEDURE DeleteLevel (dirToDelete: LongInt);
  644.         { Delete everything at and below this level. }
  645.         { The recursion stops if there's a non-recoverable error. }
  646.             VAR
  647.                 savedDir: LongInt;
  648.         BEGIN
  649.             REPEAT
  650.                 hPB.ioNamePtr := @itemName;                            { prepare to delete directory }
  651.                 hPB.ioFDirIndex := 1;                                    { get first item (every time) }
  652.                 hPB.ioDirID := dirToDelete;                            { in this directory }
  653.                 err := PBGetCatInfoSync(@hPB);
  654.                 IF err = noErr THEN                                        { did we find something? }
  655.                     BEGIN
  656.                         savedDir := dirToDelete;
  657.                         IF BTST(hPB.ioFlAttrib, fsDirFlgBit) THEN { if directory }
  658.                             BEGIN
  659.                                 savedDir := hPB.ioDirID;
  660.                                 DeleteLevel(hPB.ioDirID);                { delete its contents }
  661.                                 hPB.ioNamePtr := NIL;                    { prepare to delete directory }
  662.                             END;
  663.                         IF err = noErr THEN
  664.                             BEGIN                                                { if no errors }
  665.                                 hPB.ioDirID := savedDir;                { restore parent dirID }
  666.                                 err := PBHDeleteSync(@hPB);            { delete this item }
  667.                                 IF err = fLckdErr THEN
  668.                                     BEGIN                                        { if item was locked }
  669.                                         err := PBHRstFLockSync(@hPB);    { unlock it }
  670.                                         err := PBHDeleteSync(@hPB);    { and try again }
  671.                                     END;
  672.                             END;
  673.                     END;
  674.             UNTIL (err <> noErr);    { keep going if no errors }
  675.             IF err = fnfErr THEN        { there's nothing left at this level }
  676.                 err := noErr;            { that's OK, so pass back noErr }
  677.         END; { DeleteLevel }
  678.  
  679.     BEGIN    { DeleteDirectory }
  680.         hPB.ioVRefNum := vRefNum;
  681.         DeleteLevel(dirID);
  682.         IF err = noErr THEN
  683.             BEGIN                                                { everything below was deleted }
  684.                 hPB.ioNamePtr := NIL;
  685.                 hPB.ioDirID := dirID;
  686.                 err := PBHDeleteSync(@hPB);            { so delete dirID }
  687.                 IF err = fLckdErr THEN
  688.                     BEGIN                                        { if item was locked }
  689.                         err := PBHRstFLockSync(@hPB);    { unlock it }
  690.                         err := PBHDeleteSync(@hPB);    { and try again }
  691.                     END;
  692.             END;
  693.         DeleteDirectory := err;
  694.     END;    { DeleteDirectory }
  695.  
  696. {------------------------------------------------------------------------------}
  697.  
  698. {$S Main}
  699.     PROCEDURE UnmountAndEjectVolQueue;
  700.         VAR
  701.             volInfoPtr: volQElemPtr;
  702.             err: OSErr;
  703.             pb: HParamBlockRec;
  704.             itemHit: Integer;
  705.             alertString: Str255;
  706.             paramPos: Integer;
  707.     BEGIN
  708.         WHILE gVolQHdr.qHead <> NIL DO
  709.             BEGIN
  710.                 volInfoPtr := volQElemPtr(gVolQHdr.qHead);
  711.                 IF Dequeue(QElemPtr(volInfoPtr), @gVolQHdr) <> noErr THEN
  712.                     ; {we should never get an error, since this is the only place we dequeue}
  713.  
  714.                 { take out the net trash }
  715.                 IF (volInfoPtr^.networkVol) AND (NOT volInfoPtr^.netTrashEmpty) THEN
  716.                     BEGIN
  717.                         err := DeleteDirectory(volInfoPtr^.volumeRefNum, volInfoPtr^.netTrashDirID);
  718.                         IF err = noErr THEN
  719.                             err := ReleaseFolder(volInfoPtr^.volumeRefNum, kTrashFolderType);
  720.                     END;
  721.  
  722.                 { unmount it }
  723.                 pb.ioNamePtr := NIL;
  724.                 pb.ioVRefNum := volInfoPtr^.volumeRefNum;
  725.                 err := PBUnmountVol(@pb);
  726.                 IF err = noErr THEN
  727.                     BEGIN
  728.                         IF volInfoPtr^.ejectable AND (NOT volInfoPtr^.ejected) THEN
  729.                             BEGIN
  730.                             { and eject it }
  731.                                 pb.ioNamePtr := NIL;
  732.                                 pb.ioVRefNum := volInfoPtr^.driveNum;
  733.                                 err := PBEject(@pb);
  734.                             END;
  735.                     END
  736.                 ELSE
  737.                     BEGIN
  738.                         GetIndString(alertString, kAlertStrings, kFileBusyOnVol);
  739.                         paramPos := pos(kParam0, alertString);
  740.                         delete(alertString, paramPos, 2);
  741.                         insert(volInfoPtr^.volumeName, alertString, paramPos);
  742.                         ParamText(alertString, '', '', '');
  743.                         itemHit := Alert(kStopOK, NIL);
  744.                     END;
  745.                 DisposePtr(Ptr(volInfoPtr));
  746.             END;
  747.         gVolsEnqueued := FALSE;
  748.     END;
  749.  
  750. {------------------------------------------------------------------------------}
  751.  
  752. {$S Main}
  753.     PROCEDURE GetServerState;
  754.         VAR
  755.             index: LongInt;
  756.             scErr: OSErr;
  757.             scPB: SCParamBlockRec;
  758.     BEGIN
  759.         { get current server state }
  760.         IF gHasServerDispatch THEN
  761.             BEGIN
  762.                 gNeedToCheckForSharing := TRUE;
  763.  
  764.                 scPB.pollServerPB.scCode := SCPollServer;
  765.                 IF SyncServerDispatch(@scPB) = noErr THEN
  766.                     gServerState := scPB.pollServerPB.scServerState
  767.                 ELSE
  768.                     gServerState := SCPSDisabledwErr;     {something bad is going on,}
  769.                                                                     {but we should be able to continue.}
  770.             END;
  771.         gTurnServerBackOn := FALSE;
  772.         gNoSharedVolumes := FALSE;
  773.     END;
  774.  
  775. {------------------------------------------------------------------------------}
  776.  
  777. {$S Main}
  778.     PROCEDURE DoDiskEvents (dinfo: LongInt);
  779.     { DoDiskEvents just checks the error code from the disk mount, }
  780.     { and puts up the 'Format' dialog (through DIBadMount) if need be }
  781.     { You can do much more here if you care about what disks are }
  782.     { in the drive }
  783.         VAR
  784.             hival, loval: Integer;
  785.             tommy: Integer;
  786.             fredpoint: Point;
  787.     BEGIN
  788.         fredpoint.v := 40;
  789.         fredpoint.h := 40;
  790.         { hi word is error code, lo word is drive number }
  791.         hival := HiWord(dinfo);
  792.         loval := LoWord(dinfo);
  793.         IF hival <> noErr THEN    { something happened }
  794.             tommy := DIBadMount(fredpoint, dinfo);
  795.     END;
  796.  
  797. {------------------------------------------------------------------------------}
  798.  
  799. {$S Main}
  800.     PROCEDURE ShowHelpDialog;
  801.     { This is my sample help dialog. It doesn't do anything. Expand as you need. }
  802.         VAR
  803.             tdial: DialogPtr;
  804.             itemHit: Integer;
  805.     BEGIN
  806.         tdial := GetNewDialog(kHelp, NIL, WindowPtr(-1));
  807.         REPEAT
  808.             ModalDialog(NIL, itemhit);
  809.         UNTIL itemhit = 1;
  810.         DisposeDialog(tdial);
  811.     END;
  812.  
  813. {------------------------------------------------------------------------------}
  814.  
  815. {$S Main}
  816.     PROCEDURE DrawMain (drawit: WindowPtr);
  817.     { draws my window.  Pretty simple }
  818.     BEGIN
  819.         BeginUpdate(drawIt);
  820.         SetPort(drawIt);
  821.  
  822.         EndUpdate(drawIt);
  823.     END;
  824.  
  825. {------------------------------------------------------------------------------}
  826.  
  827. {$S Main}
  828.     PROCEDURE DoSelected (val: LongInt);
  829.     { my menu action taker }
  830.         VAR
  831.             hival, loval: Integer;
  832.             qq: Integer;
  833.             DAname: Str255;
  834.             myFSS: FSSpec;
  835.             itemHit: Integer;
  836.             alertString, paramStr0, paramStr1: Str255;
  837.     BEGIN
  838.         loval := LoWord(val);
  839.         hival := HiWord(val);
  840.  
  841.         CASE hival OF    { switch off the menu number selected }
  842.             kAppleMenu: 
  843.                 BEGIN
  844.                     IF loval <> 1 THEN    {if this was not About, it's a DA }
  845.                         BEGIN
  846.                             GetMenuItemText(gAppleMenuHandle, loval, DAname);
  847.                             qq := OpenDeskAcc(DAname);
  848.                         END
  849.                     ELSE
  850.                         qq := Alert(kAboutBox, NIL);    { do about box }
  851.                 END;
  852.             kFileMenu: 
  853.                 BEGIN
  854.                     CASE loval OF
  855.                         kUnmountItem: 
  856.                             BEGIN
  857.                                 DoCustomGetVolume(myFSS.vRefNum);
  858.                                 IF myFSS.vRefNum <> 0 THEN
  859.                                     BEGIN
  860.                                         GetServerState; { see what server is up to }
  861.                                         myFSS.parID := fsRtParID;
  862.                                         myFSS.name := '';
  863.                                         AddToVolQueue(myFSS);
  864.                                     END;
  865.                             END;
  866.                         kRestartSharing: 
  867.                             BEGIN
  868.                                 IF (numSessions > 0) THEN
  869.                                     BEGIN
  870.                                         { “There are people connected to this Macintosh.  }
  871.                                         { Do you want to disconnect them and restart }
  872.                                         { file sharing anyway?”}
  873.                                         GetIndString(alertString, kAlertStrings, kPeopleConnectedRestart);
  874.                                         ParamText(alertString, '', '', '');
  875.                                         itemHit := Alert(kCautionCancelOK, NIL);
  876.                                         IF NOT (itemHit = cancel) THEN
  877.                                             IF ShutDownTheServer = noErr THEN
  878.                                                 gRestarting := TRUE; { we'll turn it back on later }
  879.                                     END
  880.                                 ELSE IF ShutDownTheServer = noErr THEN
  881.                                     gRestarting := TRUE;{ we'll turn it back on later }
  882.                             END;
  883.                         kQuitItem: 
  884.                             gQuit := TRUE;    { only  item }
  885.                         OTHERWISE
  886.                             ;
  887.                     END; { case loval }
  888.                 END;
  889.             kHMHelpMenuID:    { Defined in Balloons }
  890.             { I only care about this item.  If anything else is returned here, I don't know what }
  891.             { it is, so I leave it alone.  Remember, the Help Manager chapter says that }
  892.             { Apple reserves the right to add and change things in the Help menu }
  893.                 BEGIN
  894.                     IF loval = gHelpItem THEN
  895.                         ShowHelpDialog;
  896.                 END;
  897.             OTHERWISE
  898.                 ;
  899.         END; { CASE hival }
  900.         HiliteMenu(0);
  901.     END;
  902.  
  903. {------------------------------------------------------------------------------}
  904.  
  905. {$S Main}
  906.     FUNCTION AEOpenHandler (messagein: AppleEvent;
  907.                                     reply: AppleEvent;
  908.                                     refIn: LongInt): OSErr;
  909.     { This is the standard Open Application event. }
  910.         VAR
  911.             theKeys: KeyMap;
  912.             alertString: Str255;
  913.             itemHit: Integer;
  914.     BEGIN
  915.         IF (gFirstAE <> kAEPrintDocuments) AND (gFirstAE <> kAEOpenDocuments) THEN
  916.             BEGIN
  917.                 gFirstAE := kAEOpenApplication;
  918.  
  919.                 { if control key is down, then restart and quit }
  920.                 GetKeys(theKeys);
  921.                 IF theKeys[$3b] THEN
  922.                     BEGIN
  923.                         GetServerState;    { see what the server is up to }
  924.                         IF (gServerState = SCPSRunning) THEN
  925.                             BEGIN
  926.                                 IF (numSessions > 0) THEN
  927.                                     BEGIN
  928.                                         { “There are people connected to this Macintosh.  }
  929.                                         { Do you want to disconnect them and restart }
  930.                                         { file sharing anyway?”}
  931.                                         GetIndString(alertString, kAlertStrings, kPeopleConnectedRestart);
  932.                                         ParamText(alertString, '', '', '');
  933.                                         itemHit := Alert(kCautionCancelOK, NIL);
  934.                                         IF NOT (itemHit = cancel) THEN
  935.                                             IF ShutDownTheServer = noErr THEN
  936.                                                 BEGIN
  937.                                                     gRestarting := TRUE; { we'll turn it back on later }
  938.                                                     gQuitAfterRestart := TRUE;
  939.                                                 END;
  940.                                     END
  941.                                 ELSE IF ShutDownTheServer = noErr THEN
  942.                                     BEGIN
  943.                                         gRestarting := TRUE;{ we'll turn it back on later }
  944.                                         gQuitAfterRestart := TRUE;
  945.                                     END;
  946.                             END;
  947.                     END;
  948.             END;
  949.  
  950.         AEOpenHandler := noErr;
  951.     END;
  952.  
  953. {------------------------------------------------------------------------------}
  954.  
  955. {$S Main}
  956.     FUNCTION AEOpenDocHandler (theAppleEvent, reply: AppleEvent;
  957.                                     handlerRefcon: LongInt): OSErr;
  958.     { Open Doc, opens our documents.  Remember, this can happen at application start AND }
  959.     { anytime else.  If your app is up and running and the user goes to the desktop, hilites one }
  960.     { of your files, and double-clicks or selects Open from the Finder File menu this event }
  961.     { handler will get called. Which means you don't do any initialization of globals here, or }
  962.     { anything else except open the doc.  }
  963.     { SO-- Do NOT assume that you are at app start time in this }
  964.     { routine, or bad things will surely happen to you. }
  965.         VAR
  966.             myFSS: FSSpec;
  967.             docList: AEDescList;
  968.             myErr: OSErr;
  969.             index, itemsInList: LongInt;
  970.             actualSize: Size;
  971.             keywd: AEKeyword;
  972.             returnedType: DescType;
  973.     BEGIN
  974.         IF (gFirstAE <> kAEOpenApplication) AND (gFirstAE <> kAEPrintDocuments) THEN
  975.             gFirstAE := kAEOpenDocuments;
  976.  
  977.         {get the direct parameter--a descriptor list--and put it into docList}
  978.         myErr := AEGetParamDesc(theAppleEvent, keyDirectObject, typeAEList, docList);
  979.         IF myErr <> noErr THEN
  980.             BEGIN
  981.                 AEOpenDocHandler := myErr;
  982.                 Exit(AEOpenDocHandler);
  983.             END;
  984.  
  985.         {count the number of descriptor records in the list}
  986.         myErr := AECountItems(docList, itemsInList);
  987.         IF myErr <> noErr THEN
  988.             BEGIN
  989.                 AEOpenDocHandler := myErr;
  990.                 Exit(AEOpenDocHandler);
  991.             END;
  992.  
  993.         IF itemsInList > 0 THEN
  994.             BEGIN
  995.                 GetServerState;    { see what the server is up to }
  996.  
  997.                 { now get each descriptor record from the list, coerce the returned }
  998.                 { data to an FSSpec record, and unmount associated}
  999.                 FOR index := 1 TO itemsInList DO
  1000.                     BEGIN
  1001.                         myErr := AEGetNthPtr(docList, index, typeFSS, keywd, returnedType, @myFSS, Sizeof(myFSS), actualSize);
  1002.                         IF myErr <> noErr THEN
  1003.                             Cycle;
  1004.                         AddToVolQueue(myFSS);
  1005.                     END;
  1006.             END;
  1007.         myErr := AEDisposeDesc(docList);
  1008.         AEOpenDocHandler := myErr;
  1009.  
  1010.         { Quit if this was dragNdrop launch, but no volumes are going to be unmounted. }
  1011.         IF (gFirstAE = kAEOpenDocuments) AND (NOT gVolsEnqueued) THEN
  1012.             gQuit := TRUE;
  1013.     END;
  1014.  
  1015. {------------------------------------------------------------------------------}
  1016.  
  1017. {$S Main}
  1018.     FUNCTION AEPrintHandler (messagein: AppleEvent;
  1019.                                     reply: AppleEvent;
  1020.                                     refIn: LongInt): OSErr;
  1021.     BEGIN
  1022.         { no printing handler in yet, so we'll ignore this }
  1023.         { the operation is functionally identical to the ODOC event, with the addition }
  1024.         { of calling your print routine.  }
  1025.         { we of course don't do anything here }
  1026.         IF (gFirstAE <> kAEOpenApplication) AND (gFirstAE <> kAEOpenDocuments) THEN
  1027.             gFirstAE := kAEPrintDocuments;
  1028.  
  1029.         AEPrintHandler := errAEEventNotHandled;    { we have no docs, so no pdoc events should come to us }
  1030.     END;
  1031.  
  1032. {------------------------------------------------------------------------------}
  1033.  
  1034. {$S Main}
  1035.     FUNCTION AEQuitHandler (messagein: AppleEvent;
  1036.                                     reply: AppleEvent;
  1037.                                     refIn: LongInt): OSErr;
  1038.     { Standard Quit event handler, to handle a Quit event from the Finder, for example.    }
  1039.     BEGIN
  1040.         { prepQuit sets the Stop flag for us.  It does _NOT_ quit, you }
  1041.         { should NEVER quit from an AppleEvent handler.  Calling }
  1042.         { ExitToShell here would blow things up }
  1043.         gQuit := TRUE;
  1044.         AEQuitHandler := noErr;
  1045.     END;
  1046.  
  1047. {------------------------------------------------------------------------------}
  1048.  
  1049. {$S Main}
  1050.     PROCEDURE DoHighLevel (AERecord: EventRecord);
  1051.     { I'm not doing error handling in this sample for clarities sake, you should. }
  1052.     { Hah, easy for me to say, huh? }
  1053.         VAR
  1054.             err: OSErr;
  1055.     BEGIN
  1056.         err := AEProcessAppleEvent(AERecord);
  1057.     END;
  1058.  
  1059. {------------------------------------------------------------------------------}
  1060.  
  1061. {$S Main}
  1062.     PROCEDURE Idling;
  1063.     BEGIN
  1064.         IF gVolsEnqueued THEN
  1065.             BEGIN
  1066.                 IF gTurnServerBackOn THEN
  1067.                     BEGIN
  1068.                                     { if the server was on, then we have to wait for the file service }
  1069.                                     { to shut down before we can unmount the volumes }
  1070.                                     { (because something was shared) }
  1071.                         IF (LongIntPtr($b50)^ = 0) OR (LongIntPtr($b50)^ = -1) THEN
  1072.                             BEGIN
  1073.                                 UnmountAndEjectVolQueue;
  1074.                                 StartTheServer;
  1075.                                 IF gFirstAE = kAEOpenDocuments THEN
  1076.                                     gQuit := TRUE;
  1077.                             END;
  1078.                     END
  1079.                 ELSE
  1080.                     BEGIN
  1081.                         UnmountAndEjectVolQueue;
  1082.                         IF gFirstAE = kAEOpenDocuments THEN
  1083.                             gQuit := TRUE;
  1084.                     END;
  1085.             END;
  1086.         IF gRestarting THEN
  1087.             BEGIN
  1088.                 IF (LongIntPtr($b50)^ = 0) OR (LongIntPtr($b50)^ = -1) THEN
  1089.                     BEGIN
  1090.                         StartTheServer;
  1091.                         gRestarting := FALSE;
  1092.                         IF gQuitAfterRestart THEN
  1093.                             gQuit := TRUE;
  1094.                     END;
  1095.             END;
  1096.     END;
  1097.  
  1098. {------------------------------------------------------------------------------}
  1099.  
  1100. {$S Main}
  1101.     PROCEDURE DoEventLoop;
  1102.     { yeah, first I took it out of the main program and now I think I'll split it up }
  1103.     { into smaller pieces.  However, it'll still be in the same source code file }
  1104.     { (don't you hate having to look all over hell for code) }
  1105.         VAR
  1106.             twindow: WindowPtr;
  1107.             evtRecord: EventRecord;
  1108.     BEGIN
  1109.         REPEAT
  1110.             IF WaitNextEvent(everyEvent, evtRecord, 30, NIL) THEN
  1111.                 CASE evtRecord.what OF
  1112.                     mouseDown:    { first, see where the hit was }
  1113.                         BEGIN
  1114.                             CASE FindWindow(Point(evtRecord.where), twindow) OF
  1115.                                 inDesk:    { if they hit in desk, then the process manager }
  1116.                                             { will switch us out, we don't need to do anything }
  1117.                                     BEGIN
  1118.                                     END;
  1119.                                 inMenuBar: 
  1120.                                     BEGIN
  1121.                                         IF (gRestarting OR gVolsEnqueued) THEN
  1122.                                             BEGIN
  1123.                                                 DisableItem(gFileMenuHandle, kUnmountItem);
  1124.                                                 DisableItem(gFileMenuHandle, kRestartSharing);
  1125.                                             END
  1126.                                         ELSE
  1127.                                             BEGIN
  1128.                                                 EnableItem(gFileMenuHandle, kUnmountItem);
  1129.                                                 GetServerState;    { see what the server is up to }
  1130.                                                 IF (gServerState = SCPSRunning) THEN
  1131.                                                     EnableItem(gFileMenuHandle, kRestartSharing)
  1132.                                                 ELSE
  1133.                                                     DisableItem(gFileMenuHandle, kRestartSharing);
  1134.                                             END;
  1135.                                         DoSelected(MenuSelect(evtRecord.where));
  1136.                                     END;
  1137.                                 inSysWindow:    { pass to the system }
  1138.                                     BEGIN
  1139.                                         SystemClick(evtRecord, twindow);
  1140.                                     END;
  1141.                                 inContent:    { handle content and control clicks here }
  1142.                                     BEGIN
  1143.                                     END;
  1144.                                 inDrag: 
  1145.                                     BEGIN
  1146.                                         IF twindow = FrontWindow THEN
  1147.                                             DragWindow(twindow, Point(evtRecord.where), qd.screenBits.bounds);
  1148.                                     END;
  1149.                                 inGrow:    { Call GrowWindow here if you have a grow box }
  1150.                                     BEGIN
  1151.                                     END;
  1152.                                 inGoAway:    { Click in Close box }
  1153.                                     BEGIN
  1154.                                     END;
  1155.                                 OTHERWISE
  1156.                                     ;
  1157.                             END;    { CASE FindWindow(evtRecord.message, twindow) }
  1158.                         END;
  1159.                     mouseUp: 
  1160.                         ;    { don't care }
  1161.                     keyDown,    { same action for key or auto key }
  1162.                     autoKey: 
  1163.                         IF BAND(evtRecord.modifiers, cmdKey) <> 0 THEN
  1164.                             DoSelected(MenuKey(CHAR(BAND(evtRecord.message, charCodeMask))));
  1165.                     keyUp: 
  1166.                         ;    { don't care }
  1167.                     updateEvt:
  1168.                         { draw whatever window needs an update }
  1169.                         DrawMain(WindowPtr(evtRecord.message));
  1170.                     diskEvt:
  1171.                         { I don't do anything special for disk events, this just passes them }
  1172.                         { to a function that checks for an error on the mount }
  1173.                         DoDiskEvents(evtRecord.message);
  1174.                     activateEvt: 
  1175.                         IF BAND(evtRecord.modifiers, activeFlag) <> 0 THEN
  1176.                             DrawMain(WindowPtr(evtRecord.message));
  1177.                     osEvt: 
  1178.                         CASE BSR(evtRecord.message, 24) OF    { high byte of message }
  1179.                             mouseMovedMessage: 
  1180.                                 ;    { don't care }
  1181.                             suspendResumeMessage:    {suspend/resume is also an activate/deactivate }
  1182.                                 gInBackground := BAND(evtRecord.message, resumeFlag) = 0;
  1183.                             OTHERWISE
  1184.                                 ;
  1185.                         END;
  1186.                     kHighLevelEvent: 
  1187.                         DoHighLevel(evtRecord);
  1188.                     OTHERWISE
  1189.                         ;
  1190.                 END    { CASE evtRecord.what }
  1191.             ELSE
  1192.                 Idling; { do my idle stuff }
  1193.         UNTIL gQuit = TRUE;
  1194.     END;    { DoEventLoop }
  1195.  
  1196. {------------------------------------------------------------------------------}
  1197.  
  1198. {$S Initialize}
  1199.     PROCEDURE WhatWeGot;
  1200.         CONST
  1201.             kNewFileSharingVersion = $3e;    { File Sharing 4.0's version number }
  1202.         VAR
  1203.             vers: LongInt;
  1204.             err: OSErr;
  1205.             aLong: LongInt;
  1206.             scPB: SCParamBlockRec;
  1207.             itemHit: Integer;
  1208.             alertString: Str255;
  1209.     BEGIN
  1210.         { Check system version }
  1211.         vers := 0;
  1212.         err := Gestalt(gestaltSystemVersion, vers);
  1213.         IF LoWord(vers) < $0700 THEN
  1214.             BEGIN
  1215.                 GetIndString(alertString, kAlertStrings, kBadSystem);
  1216.                 ParamText(alertString, '', '', '');
  1217.                 itemHit := Alert(kStopOK, NIL);
  1218.                 ExitToShell;
  1219.             END;
  1220.  
  1221.         { Check this machine for AppleEvents. }
  1222.         { If they are not here (i.e., not 7.0) then we exit }
  1223.         gHasAppleEvents := Gestalt(gestaltAppleEventsAttr, aLong) = noErr;
  1224.         IF NOT gHasAppleEvents THEN
  1225.             BEGIN
  1226.                 GetIndString(alertString, kAlertStrings, kNoAppleEvents);
  1227.                 ParamText(alertString, '', '', '');
  1228.                 itemHit := Alert(kStopOK, NIL);
  1229.                 ExitToShell;
  1230.             END;
  1231.  
  1232.         { Check this machine for a file server. }
  1233.         { If no file server, then exit. }
  1234.         gHasServerDispatch := TrapAvailable(ServerDispatch);
  1235.  
  1236.         IF gHasServerDispatch THEN
  1237.             BEGIN
  1238.                 { get server version.}
  1239.                 scPB.versionPB.scCode := SCServerVersion;
  1240.                 scPB.versionPB.scExtNamePtr := NIL;
  1241.                 {pretend server isn't there if error or the server is not file sharing}
  1242.                 { v1.2 - or we have the new File Sharing which lets you unmount volumes without }
  1243.                 { killing the server first. }
  1244.                 err := SyncServerDispatch(@scPB);
  1245.                 IF (err <> noErr) OR
  1246.                    (scPB.versionPB.scServerType <> MFSType) OR
  1247.                    (scPB.versionPB.scServerVersion >= kNewFileSharingVersion) THEN
  1248.                     gHasServerDispatch := FALSE;
  1249.                 
  1250.                 IF (err = noErr) AND (scPB.versionPB.scServerType <> MFSType) THEN
  1251.                 BEGIN
  1252.                     GetIndString(alertString, kAlertStrings, kIsAppleShareServer);
  1253.                     ParamText(alertString, '', '', '');
  1254.                     itemHit := Alert(kStopOK, NIL);
  1255.                     ExitToShell;
  1256.                 END;
  1257.                 
  1258.                 IF (err = noErr) AND (scPB.versionPB.scServerVersion >= kNewFileSharingVersion) THEN
  1259.                 BEGIN
  1260.                     GetIndString(alertString, kAlertStrings, kIsNewFileSharingServer);
  1261.                     ParamText(alertString, '', '', '');
  1262.                     itemHit := Alert(kStopOK, NIL);
  1263.                     ExitToShell;
  1264.                 END;
  1265.             END;
  1266.  
  1267.         err := GetCurrentProcess(gOurSN);    { Get our process serial number for later use, if needed }
  1268.     END;
  1269.  
  1270. {------------------------------------------------------------------------------}
  1271.  
  1272. {$S Initialize}
  1273.     PROCEDURE DoSetupMenus;
  1274.         VAR
  1275.             helpHandle: MenuHandle;
  1276.             helpString: StringHandle;
  1277.             count: Integer;
  1278.     BEGIN
  1279.         gMymenu := GetNewMBar(kMBarID);
  1280.         SetMenuBar(gMymenu);
  1281.         gAppleMenuHandle := GetMenuHandle(kAppleMenu);
  1282.         gFileMenuHandle := GetMenuHandle(kFileMenu);
  1283.         AppendResMenu(gAppleMenuHandle, 'DRVR');
  1284.  
  1285.         { now install my Help menu item in the Help Manager's menu }
  1286.         IF HMGetHelpMenuHandle(helpHandle) = noErr THEN    { Get the Help menu handle }
  1287.             BEGIN
  1288.                 count := CountMItems(helpHandle);    { How many items are there? }
  1289.                 helpString := GetString(kHelp);{ get my help menu item string }
  1290.                 DetachResource(Handle(helpString));    { detach it }
  1291.                 HNoPurge(Handle(helpString));
  1292.                 MoveHHi(Handle(helpString));
  1293.                 HLock(Handle(helpString));
  1294.                 InsertMenuItem(helpHandle, helpString^^, count + 1);    { insert my item in the Help menu }
  1295.                 gHelpItem := CountMItems(helpHandle);    { The number of the item }
  1296.             END
  1297.         ELSE
  1298.             gHelpItem := 0;    { error - set it to something that we'll never see }
  1299.  
  1300.         DrawMenuBar;
  1301.     END;
  1302.  
  1303. {------------------------------------------------------------------------------}
  1304.  
  1305. {$S Initialize}
  1306.     PROCEDURE InitAEStuff;
  1307.         VAR
  1308.             alertString: Str255;
  1309.             itemHit: Integer;
  1310.     BEGIN
  1311.         { The following series of calls installs all our AppleEvent Handlers. }
  1312.         { These handlers are added to the application event handler list that }
  1313.         { the AppleEvent manager maintains.  So, whenever an AppleEvent happens }
  1314.         { and we call AEProcessEvent, the AppleEvent manager will check our }
  1315.         { list of handlers and dispatch to it if there is one. }
  1316.         IF (AEInstallEventHandler(kCoreEventClass, kAEOpenApplication, @AEOpenHandler, 0, false) <> noErr) | (AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, @AEOpenDocHandler, 0, false) <> noErr) | (AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, @AEQuitHandler, 0, false) <> noErr) | (AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments, @AEPrintHandler, 0, false) <> noErr) THEN        { <- the short-circuit OR lets us bail on first error }
  1317.             BEGIN
  1318.                 GetIndString(alertString, kAlertStrings, kAEHandlerInstallError);
  1319.                 ParamText(alertString, '', '', '');
  1320.                 itemHit := Alert(kStopOK, NIL);
  1321.                 ExitToShell;
  1322.             END;
  1323.     END;
  1324.  
  1325. {------------------------------------------------------------------------------}
  1326.  
  1327. {$S Initialize}
  1328.     PROCEDURE InitializeApp;
  1329.     BEGIN
  1330.         MaxApplZone;
  1331.         { MoreMasters go here if you need'm }
  1332.         InitGraf(@qd.thePort);
  1333.         InitFonts;
  1334.         InitWindows;
  1335.         InitMenus;
  1336.         TEInit;
  1337.         InitDialogs(NIL);
  1338.         InitCursor;
  1339.  
  1340.         WhatWeGot;    { make sure everything we need is available }
  1341.  
  1342.         InitAEStuff;
  1343.  
  1344.         DoSetupMenus;    { set up my menus }
  1345.  
  1346.         { Initialize the rest of my globals that aren't initialized elsewhere! }
  1347.  
  1348.         gQuit := FALSE;
  1349.         gInBackground := FALSE;
  1350.  
  1351.         IF NOT gHasServerDispatch THEN
  1352.             gServerState := SCPSJustDisabled; { server isn't installed - same as disabled }
  1353.         gNeedToCheckForSharing := FALSE;
  1354.         gTurnServerBackOn := FALSE;
  1355.         gNoSharedVolumes := TRUE;
  1356.         gRestarting := FALSE;
  1357.         gQuitAfterRestart := FALSE;
  1358.  
  1359.         { clear the volume queue }
  1360.         gVolsEnqueued := FALSE;
  1361.         gVolQHdr.QHead := NIL;
  1362.         gVolQHdr.QTail := NIL;
  1363.         
  1364.         { It was like this }
  1365.         { gFirstAE := Concat(chr(0), chr(0), chr(0), chr(0)); } { zero it out }
  1366.         gFirstAE[1] := chr(0); { zero it out }
  1367.         gFirstAE[2] := chr(0); { zero it out }
  1368.         gFirstAE[3] := chr(0); { zero it out }
  1369.         gFirstAE[4] := chr(0); { zero it out }
  1370.     END;
  1371.  
  1372. {------------------------------------------------------------------------------}
  1373.  
  1374. { PROCEDURE _DataInit;}
  1375. { External;}
  1376. { this is the application initialization code }
  1377.  
  1378. {==============================================================================}
  1379.  
  1380. {$S Main}
  1381. BEGIN
  1382.     { UnloadSeg(@_DataInit);}
  1383.     { throw out the setup code }
  1384.     InitializeApp;
  1385.     { UnloadSeg(@InitializeApp); }
  1386.     { get rid of my initialization code }
  1387.     DoEventLoop;
  1388. END.